package pacman;
import java.io.IOException;
import java.util.HashMap;

import communication.Communicator;

import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;
import simulation.Simulator;

public abstract class Robot {
	private Location location; //The location the robot thinks he is (but not necessarily is)	
	private int rotation; //The rotation the robot thinks he has (but not necessarily has)
	private Maze maze; //The maze as the robot sees it (can be incomplete and/or inaccurate)
	protected HashMap<String,Maze> mazesOtherRobots;
	private Communicator communicator;
	private Simulator simulator;
	
	public Location getLocation() {
		return location;
	}

	protected void setLocation(Location location) {
		this.location = location;
	}
	
	public void setSimulator(Simulator simulator) {
		this.simulator = simulator;
	}
	
	public Simulator getSimulator(){
		return simulator;
	}
	protected void setOtherMazes(HashMap<String, Maze> mazes){
		this.mazesOtherRobots = mazes;
	}
	/**
	 * Returns the integer value of this Ghost's direction.
	 * "Hierbij wordt de waarde 
	 * 1 gebruikt voor noord naar zuid,  == ZUID
	 * 2 voor oost naar west, 			 == WEST
	 * 3 voor zuid naar noord, 			 == NOORD
	 * 4 voor west naar oost."			 == OOST
	 */
	public Direction generateDirection(int rotation) {
		int rot = rotation % 360;
		rot = rot < 0 ? (360 - Math.abs(rot)) : rot;
		Direction direction;
		if (rot > 45 && rot < 135) {
			direction = Direction.NORTH;
		} else if (rot >= 135 && rot < 225) {
			direction = Direction.WEST;
		} else if (rot >= 225 && rot < 315) {
			direction = Direction.SOUTH;
		} else {
			direction = Direction.EAST;
		}
		return direction;
	}
	/**
	 * 
	 * @param distance
	 * @param ignoreInSimulation wordt op true gezet bij correctiemethodes in fysieke robot omdat anders de simulatie niet meer overeenkomt met werkelijkheid
	 * 							 (want simulatie houdt geen rekening met afwijkingen)
	 */
	public void travel(int distance, boolean ignoreInSimulation){
		
		if(communicator != null)
		{
			communicator.travel(distance);
			if(communicator.isPressed()){
				communicator.travel(-10);
				communicator.isPressed(); // om de touchsensor eventueel te resetten
				Coordinate currentCoordinate = maze.getCoordinateOfLocation(this.getLocation());
				maze.write(currentCoordinate.x,currentCoordinate.y,generateDirection(this.rotation) , (char)1, true);
				return;
			}
		}
		
		//TODO: ONLY WORKS FOR GHOSTS, NOT FOR PACMAN!!!
		//simulator verplaatsing simuleren
		try {
			if(!ignoreInSimulation)
				simulator.SimulateTravel((Ghost) this, distance);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//lokale locatie updaten (de robot denkt de volledige distance echt afgelegd te hebben, ongeacht er een muur in de weg staat of niet)
		if(!ignoreInSimulation){
			double rotRad = Math.toRadians(getRotation());
			double newX = location.getX() + Math.cos(rotRad) * distance;
			double newY = location.getY() + Math.sin(rotRad) * distance;
			this.setLocation(new Location(newX, newY));	
			try{((Ghost)this).sendPOSITION();}catch(IOException ioe) {ioe.printStackTrace();}
		}
	}

	/**
	 * Rotation is measured to the positive x axis.  So a positive rotation is counterclockwise
	 * @param degrees
	 * @param ignoreInSimulation wordt true gezet bij correctiemethodes, omdat anders uw simulatie niet meer overeenkomt met werkelijkheid
	 */
	public void rotate(int degrees, boolean ignoreInSimulation){
		if(communicator != null)
		{
			communicator.rotateRobot(degrees);
		}
		
		//TODO: ONLY WORKS FOR GHOSTS, NOT FOR PACMAN!!!
		//simulator verplaatsing simuleren
		if(!ignoreInSimulation){
			simulator.SimulateRotation((Ghost) this, degrees);


			//lokale rotatie updaten
			setRotation((getRotation()+degrees)%360);
		}
		
	}
	
	public int getRotation() {
		return rotation;
	}
		
	protected void setRotation(int rotation) {
		this.rotation = rotation;
	}

	public Maze getMaze() {
		return maze;
	}

	protected void setMaze(Maze maze) {
		this.maze = maze;
	}

	protected void setCommunicator(Communicator comm)
	{
		communicator = comm;
	}
	
	public Communicator getCommunicator()
	{
		return communicator;
	}
	public Maze getMazeOnName(String name){
		return mazesOtherRobots.get(name);
	}
	public HashMap<String, Maze> getAllMazes(){
		return mazesOtherRobots;
	}
	public void setOtherRobot(String name){
		Maze newMaze = new Maze(10);
		if(checkNameNotExists(name)){
			this.mazesOtherRobots.put(name, newMaze);
		}else{
			System.err.println("naam zat al in map");
		}
			
	}
	private boolean checkNameNotExists(String name){
		if(name ==null)
			return false;
		else if(getAllMazes().containsKey(name))
			return false;
		else
			return true;
			
	}
}
